﻿using ControllerBase = MicroCloud.AspNetCore.Mvc.ControllerBase;

namespace MicroCloud.AspNetCore
{
    /// <summary>
    /// MVC相关扩展方法
    /// </summary>
    public static class MvcExtensions
    {
        #region "扩展方法"
        #region "判断类型是否是Controller"
        /// <summary>
        /// 判断类型是否是Controller
        /// </summary>
        /// <param name="type"></param>
        /// <param name="isAbstract"></param>
        /// <returns></returns>
        public static bool IsController(this Type type, bool isAbstract = false)
        {
            Check.NotNull(type, nameof(type));

            return IsController(type.GetTypeInfo(), isAbstract);
        }
        #endregion
        #region "判断类型是否是Controller"
        /// <summary>
        /// 判断类型是否是Controller
        /// </summary>
        /// <param name="typeInfo"></param>
        /// <param name="isAbstract"></param>
        /// <returns></returns>
        public static bool IsController(this TypeInfo typeInfo, bool isAbstract = false)
        {
            Check.NotNull(typeInfo, nameof(typeInfo));

            return typeInfo.IsClass && (isAbstract || !typeInfo.IsAbstract) && typeInfo.IsPublic && !typeInfo.ContainsGenericParameters
                 && !typeInfo.IsDefined(typeof(NonControllerAttribute)) && (typeInfo.Name.EndsWithIgnoreCase("Controller")
                    || typeInfo.IsDefined(typeof(ControllerAttribute))) && typeInfo.IsDefined(typeof(ApiControllerAttribute));
        }
        #endregion

        #region "获取Area名"
        /// <summary>
        /// 获取Area名
        /// </summary>
        /// <param name="routeData"></param>
        /// <returns></returns>
        public static string GetAreaName(this RouteData routeData)
        {
            Check.NotNull(routeData, nameof(routeData));

            string area = null;
            if (routeData.Values.TryGetValue("area", out object value))
            {
                area = (string)value;
                if (area.IsNullOrWhiteSpace())
                {
                    area = null;
                }
            }
            return area;
        }
        #endregion
        #region "获取Controller名"
        /// <summary>
        /// 获取Controller名
        /// </summary>
        /// <param name="routeData"></param>
        /// <returns></returns>
        public static string GetControllerName(this RouteData routeData)
        {
            Check.NotNull(routeData, nameof(routeData));

            return routeData.Values["controller"].ToString();
        }
        #endregion
        #region "获取Action名"
        /// <summary>
        /// 获取Action名
        /// </summary>
        /// <param name="routeData"></param>
        /// <returns></returns>
        public static string GetActionName(this RouteData routeData)
        {
            Check.NotNull(routeData, nameof(routeData));

            return routeData.Values["action"].ToString();
        }
        #endregion

        #region "获取正在执行的Action的相关功能信息"
        /// <summary>
        /// 获取正在执行的Action的相关功能信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static IFunction GetExecuteFunction(this HttpContext context)
        {
            Check.NotNull(context, nameof(context));

            IServiceProvider provider = context.RequestServices;
            ScopedDictionary dict = provider.GetService<ScopedDictionary>();

            //TODO:这玩意儿貌似不能获取最新的数据，注释后从功能缓存中获取
            //if (dict.Function != null)
            //{
            //    return dict.Function;
            //}

            var routeData = context.GetRouteData();

            string area = routeData.GetAreaName();
            string controller = routeData.GetControllerName();
            string action = routeData.GetActionName();
            // todo: 当权限模块没启用时，应取消权限验证，如何判断权限模块已启用？
            IFunctionHandler functionHandler = provider.GetService<IFunctionHandler>();
            if (functionHandler == null)
            {
                return null;
            }
            IFunction function = functionHandler.GetFunction(area, controller, action);
            if (function != null)
            {
                dict.Function = function;
            }
            return function;
        }
        #endregion

        #region "获取Area名"
        /// <summary>
        /// 获取Area名
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetAreaName(this ActionContext context)
        {
            Check.NotNull(context, nameof(context));

            return context.RouteData.GetAreaName();
        }
        #endregion
        #region "获取Controller名"
        /// <summary>
        /// 获取Controller名
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetControllerName(this ActionContext context)
        {
            Check.NotNull(context, nameof(context));

            return context.RouteData.GetControllerName();
        }
        #endregion
        #region "获取Action名"
        /// <summary>
        /// 获取Action名
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static string GetActionName(this ActionContext context)
        {
            Check.NotNull(context, nameof(context));

            return context.RouteData.GetActionName();
        }
        #endregion
        #region "获取正在执行的Action的相关功能信息"
        /// <summary>
        /// 获取正在执行的Action的相关功能信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static IFunction GetExecuteFunction(this ActionContext context)
        {
            Check.NotNull(context, nameof(context));

            IServiceProvider provider = context.HttpContext.RequestServices;
            ScopedDictionary dict = provider.GetService<ScopedDictionary>();

            //TODO:这玩意儿貌似不能获取最新的数据，注释后从功能缓存中获取
            //if (dict.Function != null)
            //{
            //    return dict.Function;
            //}

            string area = context.GetAreaName();
            string controller = context.GetControllerName();
            string action = context.GetActionName();
            // todo: 当权限模块没启用时，应取消权限验证，如何判断权限模块已启用？
            IFunctionHandler functionHandler = provider.GetService<IFunctionHandler>();
            if (functionHandler == null)
            {
                return null;
            }
            IFunction function = functionHandler.GetFunction(area, controller, action);
            if (function != null)
            {
                dict.Function = function;
            }
            return function;
        }
        #endregion

        #region "获取当前Controller中正在操作的Action的相关功能信息"
        /// <summary>
        /// 获取当前Controller中正在操作的Action的相关功能信息
        /// </summary>
        /// <param name="controller"></param>
        /// <returns></returns>
        public static IFunction GetExecuteFunction(this ControllerBase controller)
        {
            Check.NotNull(controller, nameof(controller));

            return controller.ControllerContext.GetExecuteFunction();
        }
        #endregion
        #region "获取指定URL的功能信息"
        /// <summary>
        /// 获取指定URL的功能信息
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static IFunction GetFunction(this ControllerBase controller, string url)
        {
            Check.NotNull(url, nameof(url));

            url = url.StartsWith("https://") || url.StartsWith("http://")
                ? new Uri(url).AbsolutePath : !url.StartsWith('/') ? $"/{url}" : url;
            IServiceProvider provider = controller.HttpContext.RequestServices;
            IHttpContextFactory factory = provider.GetService<IHttpContextFactory>();
            HttpContext httpContext = factory.Create(controller.HttpContext.Features);
            httpContext.Request.Path = url;
            httpContext.Request.Method = "POST";
            RouteContext routeContext = new(httpContext);
            IRouteCollection router = controller.RouteData.Routers.OfType<IRouteCollection>().FirstOrDefault();
            if (router == null)
            {
                return null;
            }
            router.RouteAsync(routeContext).Wait();
            if (routeContext.Handler == null)
            {
                return null;
            }
            RouteValueDictionary dict = routeContext.RouteData.Values;
            string areaName = dict.GetOrDefault("area")?.ToString();
            string controllerName = dict.GetOrDefault("controller")?.ToString();
            string actionName = dict.GetOrDefault("action")?.ToString();
            IFunctionHandler handler = provider.GetService<IFunctionHandler>();
            return handler?.GetFunction(areaName, controllerName, actionName);
        }
        #endregion
        #region "检测当前用户是否拥有指定URL的功能权限"
        /// <summary>
        /// 检测当前用户是否拥有指定URL的功能权限
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public static bool CheckFunctionAuth(this ControllerBase controller, string url)
        {
            Check.NotNull(controller, nameof(controller));
            Check.NotNull(url, nameof(url));

            IFunction function = controller.GetFunction(url);
            if (function == null)
            {
                return false;
            }
            IFunctionAuthorization authorization = controller.HttpContext.RequestServices.GetService<IFunctionAuthorization>();
            return authorization.Authorize(function, controller.User).IsOk;
        }
        #endregion
        #region "检测当前用户是否有指定功能的功能权限"
        /// <summary>
        /// 检测当前用户是否有指定功能的功能权限
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="actionName"></param>
        /// <param name="controllerName"></param>
        /// <param name="areaName"></param>
        /// <returns></returns>
        public static bool CheckFunctionAuth(this ControllerBase controller, string actionName, string controllerName, string areaName = null)
        {
            Check.NotNull(controller, nameof(controller));

            IServiceProvider provider = controller.HttpContext.RequestServices;
            IFunctionHandler functionHandler = provider.GetService<IFunctionHandler>();
            IFunction function = functionHandler?.GetFunction(areaName, controllerName, actionName);
            if (function == null)
            {
                return false;
            }
            IFunctionAuthorization authorization = provider.GetService<IFunctionAuthorization>();
            return authorization.Authorize(function, controller.User).IsOk;
        }
        #endregion

        #endregion

    }
}